package mysql

import (
	"context"

	v1 "gitee.com/youkelike/ziam/zapi/apiserver/v1"
	metav1 "gitee.com/youkelike/ziam/zapi/meta/v1"
	"gitee.com/youkelike/ziam/zcode/zcode"
	"gitee.com/youkelike/ziam/zerrors"
	"gitee.com/youkelike/ziam/zutils/gormutil"
	"gorm.io/gorm"
)

type Policies struct {
	db *gorm.DB
}

func newPolicies(ds *dataStore) *Policies {
	return &Policies{ds.db}
}

func (p *Policies) Create(ctx context.Context, policy *v1.Policy, opts metav1.CreateOptions) error {
	return p.db.Create(&policy).Error
}

func (p *Policies) Update(ctx context.Context, policy *v1.Policy, opts metav1.UpdateOptions) error {
	return p.db.Save(&policy).Error
}

func (p *Policies) Delete(ctx context.Context, username, name string, opts metav1.DeleteOptions) error {
	if opts.Unscoped {
		p.db = p.db.Unscoped()
	}

	err := p.db.Where("username = ? and name = ?", username, name).Delete(&v1.Policy{}).Error
	if err != nil && !zerrors.Is(err, gorm.ErrRecordNotFound) {
		return zerrors.WithCode(zcode.ErrDatabase, err.Error())
	}

	return nil
}

func (p *Policies) DeleteByUser(ctx context.Context, username string, opts metav1.DeleteOptions) error {
	if opts.Unscoped {
		p.db = p.db.Unscoped()
	}

	return p.db.Where("username = ?", username).Delete(&v1.Policy{}).Error
}

func (p *Policies) DeleteCollection(ctx context.Context, username string, names []string, opts metav1.DeleteOptions) error {
	if opts.Unscoped {
		p.db = p.db.Unscoped()
	}

	return p.db.Where("username = ? and name in (?)", username, names).Delete(&v1.Policy{}).Error
}

func (p *Policies) DeleteCollectionByUser(ctx context.Context, usernames []string, opts metav1.DeleteOptions) error {
	if opts.Unscoped {
		p.db = p.db.Unscoped()
	}

	return p.db.Where("username in (?)", usernames).Delete(&v1.Policy{}).Error
}

func (p *Policies) Get(ctx context.Context, username, name string, opts metav1.GetOptions) (*v1.Policy, error) {
	policy := &v1.Policy{}
	err := p.db.Where("username = ? and name = ?", username, name).First(&policy).Error
	if err != nil {
		if zerrors.Is(err, gorm.ErrRecordNotFound) {
			return nil, zerrors.WithCode(zcode.ErrPolicyNotFound, err.Error())
		}

		return nil, zerrors.WithCode(zcode.ErrDatabase, err.Error())
	}

	return policy, nil
}

func (p *Policies) List(ctx context.Context, username string, opts metav1.ListOptions) (*v1.PolicyList, error) {
	ret := &v1.PolicyList{}
	ol := gormutil.GetLimitAndOffset(opts.Offset, opts.Limit)

	if username != "" {
		p.db = p.db.Where("username = ?", username)
	}

	d := p.db.Offset(ol.Offset).Limit(ol.Limit).Order("id desc").Find(&ret.Items).Count(&ret.TotalCount)

	return ret, d.Error
}
